package ldk.apps.compass

import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.util.Log
import java.util.*

/**
 * 指南针
 * 数据的处理者
 * Created by ldkxingzhe@163.com on 2017/12/20.
 */
class Compass(private val sensorManager: SensorManager): SensorEventListener {
    private val TAG = "Compass"

    private val rotationMatrix = FloatArray(9)
    private val orientationAngles = FloatArray(3)

    private var magnetometerSensor: Sensor? = null    // 地磁传感器
    private var accelerometerSensor: Sensor? = null   // 加速度传感器

    private var magnetometerValues: FloatArray? = null
    private var accelerometerValues: FloatArray? = null

    private var workerThread = HandlerThread("compass-worker")
    private val workerValueList = LinkedList<Int>()
    private val workerHandler: WorkerHandler
    private val mainHandler: Handler

    init {
        workerThread.start()
        workerHandler = WorkerHandler(workerThread.looper)
        mainHandler = Handler(Looper.getMainLooper())
    }

    var callback: ((Int)-> Unit)? = null

    /**
     * 判断是否有地磁传感器
     */
    fun findMagnetometersSensor(): Boolean{
        magnetometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
        return magnetometerSensor != null
    }

    /**
     * 判断是否有加速度传感器
     */
    fun findAccelerometerSensor(): Boolean{
        accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        return accelerometerSensor != null
    }

    fun registerListener(){
        sensorManager.registerListener(this, magnetometerSensor, SensorManager.SENSOR_DELAY_NORMAL)
        sensorManager.registerListener(this, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL)
        workerHandler.startTimer()
    }

    fun unRegisterListener(){
        sensorManager.unregisterListener(this, magnetometerSensor)
        sensorManager.unregisterListener(this, accelerometerSensor)
        workerHandler.stopTimer()
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        Log.d(TAG, "onAccuracyChanged: $sensor, $accuracy")
    }

    override fun onSensorChanged(event: SensorEvent) {
        if (event.sensor == magnetometerSensor){
            magnetometerValues = event.values
        }else{
            accelerometerValues = event.values
        }
        if (magnetometerValues == null || accelerometerValues == null)
            return
        SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magnetometerValues)
        SensorManager.getOrientation(rotationMatrix, orientationAngles)
        val value = (orientationAngles[0] * 180 / Math.PI).toInt()
//        Log.v(TAG, "value is $value")
        workerHandler.post {
            workerValueList.add(value)
        }
    }

    inner class WorkerHandler(looper: Looper): Handler(looper) {
        val orientationChanged = 0
        var lastValue: Int = 0

        fun startTimer(){
            stopTimer()
            sendEmptyMessage(orientationChanged)
        }

        fun stopTimer(){
            removeMessages(orientationChanged)
        }

        override fun handleMessage(msg: Message?) {
            when(msg!!.what){
                orientationChanged-> {
                    if (workerValueList.isNotEmpty()){
                        val average = workerValueList.average()
                        val variance = workerValueList.map { Math.pow(it - average, 2.0) }.average()
                        val reverseList = workerValueList.map { if (it < 0) -it -180 else 180 - it }
                        val reverseAverage = reverseList.average()
                        val reverseVariance = reverseList.map { Math.pow(it - reverseAverage, 2.0) }.average()
                        val realAverage = when {
                            reverseVariance >= variance -> average
                            reverseAverage < 0 -> -reverseAverage - 180
                            else -> 180 - reverseAverage
                        }.toInt()
//                        Log.v(TAG, "variance is $variance, and reverseVariance is $reverseVariance")

                        if (lastValue != realAverage && callback != null){
                            mainHandler.post {
                                callback!!.invoke(realAverage)
                            }
                            lastValue = realAverage
                        }
                        workerValueList.clear()
                    }
                    sendEmptyMessageDelayed(orientationChanged, 500)
                }
            }
        }
    }
}